-
Notifications
You must be signed in to change notification settings - Fork 378
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: dynamic gas price, keeper implementation #2838
base: master
Are you sure you want to change the base?
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #2838 +/- ##
==========================================
+ Coverage 60.87% 60.98% +0.11%
==========================================
Files 563 568 +5
Lines 75193 75606 +413
==========================================
+ Hits 45770 46111 +341
- Misses 26055 26085 +30
- Partials 3368 3410 +42
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here are some initial questions and comments per our conversation earlier.
@@ -107,9 +110,11 @@ func NewAppWithOptions(cfg *AppOptions) (abci.Application, error) { | |||
func(ctx sdk.Context, tx std.Tx, simulate bool) ( | |||
newCtx sdk.Context, res sdk.Result, abort bool, | |||
) { | |||
// Add last gas price in the context | |||
ctx = ctx.WithValue(auth.GasPriceContextKey{}, gpKpr.LastGasPrice(ctx)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe a refactor is necessary generally, for this and other key-value pairs being put in the std.Context instance, but it seems like data that is always necessary should be part of the Context struct's definition. This approach still makes sense for storing key-value pairs for module data that is not required.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this approach still makes sense for storing key-value pairs for module data that is not required.
Good point. We can address the refactor in a separate PR later
|
||
func DefaultGenState() GnoGenesisState { | ||
authGen := auth.DefaultGenesisState() | ||
gp, err := std.ParseGasPrice(InitGasPrice) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can InitGasPrice
be a GasPrice
struct so it doesn't need to be parsed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
InitGasPrice is a default genesis value declared as a constant. Go only allows primitive types for constants.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with Dylan, it doesn't make sense to parse it every time, when we can just declare it as a global var
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the PR looks alright 💯
The way we transfer params, and load latest gas values through keepers in different modules is not a pattern we should be using, but I realize that the alternatives require much, much chunkier changes to how modules communicate.
There needs to be some heavy rebasing done with master
, but I haven't seen anything blocking in the PR. We should be good
contribs/gnodev/go.mod
Outdated
@@ -1,6 +1,6 @@ | |||
module github.com/gnolang/gno/contribs/gnodev | |||
|
|||
go 1.22 | |||
go 1.22.0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can skip the minor version
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it is to fix the issue of being unable to download the toolchain. I just realized that adding toolchain go1.22.4 is a better solution.
@@ -104,6 +105,9 @@ var ( | |||
// BlockSizeBytes measures the size of the latest block in bytes | |||
BlockSizeBytes metric.Int64Histogram | |||
|
|||
// BlockGasPriceAmount measures the block gas price of the last block | |||
BlockGasPriceAmount metric.Int64Histogram |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good call on also adding this as a metric :)
tm2/pkg/std/gasprice.go
Outdated
@@ -48,3 +53,28 @@ func ParseGasPrices(gasprices string) (res []GasPrice, err error) { | |||
} | |||
return res, nil | |||
} | |||
|
|||
// IsGTE compares the GasPrice with another gas price B. If coin denom matches AND fee per gas is | |||
// greater or equal to the gas price B return true, other wise return false, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo here, otherwise
tm2/pkg/std/gasprice.go
Outdated
// greater or equal to the gas price B return true, other wise return false, | ||
func (gp GasPrice) IsGTE(gpB GasPrice) bool { | ||
if gp.Price.Denom != gpB.Price.Denom { | ||
panic(fmt.Sprintf("gas price denominations should be equal; %s, %s", gp.Price.Denom, gpB.Price.Denom)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want the panics here? Where are they caught?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Gas price is compared in sdk/ante handler. The panic is caught in sdk/base app for all transactions.
However, I think it would be better to return error instead of paniking, as this involves user input.
I will fix it.
prod1 := big.NewInt(0).Mul(gpa, gpBg) // gp's price amount * gpB's gas | ||
prod2 := big.NewInt(0).Mul(gpg, gpBa) // gpB's gas * pg's price amount | ||
// This is equivalent to checking | ||
// That the Fee / GasWanted ratio is greater than or equal to the minimum GasPrice per gas. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
num.Div(num, targetGasInt) | ||
num.Div(num, denom.SetInt64(c)) | ||
// increase at least 1 | ||
diff := max(num, big.NewInt(1)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it makes sense to optimize this a bit by defining a common var bigOne = big.NewInt(1)
|
||
// targetGas = maxGax*TargetGasRatio/100 | ||
|
||
num.Mul(big.NewInt(maxGas), big.NewInt(params.TargetGasRatio)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have you thought about using holiman's uint256, instead of big.Int
?
num = max(num, initPriceInt) | ||
} | ||
|
||
if !num.IsInt64() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When is this possible?
If it is, why don't we just set the biggest possible gas price that's relevant?
} | ||
|
||
// max returns the larger of x or y. | ||
func max(x, y *big.Int) *big.Int { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick: maybe consider naming this maxBig
Txs []std.Tx `json:"txs"` | ||
Balances []Balance `json:"balances"` | ||
Txs []std.Tx `json:"txs"` | ||
Auth auth.GenesisState `json:"auth"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This has been added as part of Manfred's recent PR to master
Context
This PR is inspired by EIP-1559 and adjusts the gas price based on the ratio of gas used in the last block compared to the target block gas. The gas price is enforced globally across the network. However, validators can still configure a minimum gas price (min-gas-price) to reject low-fee transactions and prevent mempool spam. A higher gas price will take precedence when configured.
Current implementation is an alternative to PR2544 and is based on the feedbacks.
Here are the main differences:
High level flow
Start a new node from genesis. The initial gas price and formula parameters are saved in the genesis and loaded into the params keeper and gas keeper.
When a node receives a new transaction, the application checks if the user has provided sufficient fees for the transaction. It will reject the transaction if it does not meet the gas price set by the network and individual nodes.
The node processes the entire block during the proposal, voting, and restart phases.
The GasPriceKeeper will calculate and update the gas price according to the formula in the application’s EndBlock() function.
Formular
The compressor is used to reduce the impact on price caused by sudden changes in the gas used within a block
When the last gas used in a block is above the target gas, we increase the gas price
When the last gas used in a block is below the target gas, we decrease the gas price until it returns to the initial gas price in the block.
Impact
The Cosmos SDK has an optional setting for a minimum gas price. Each validator can configure their own values to only accept transactions with a gas price that meets their setting in the mempool. When a user submits a transaction on-chain, the gas price is calculated as gas-fee / gas-wanted.
With the addition of the block gas price, a network-wide minimum gas price is enforced for every validator. Users will need to provide a gas price that meets the requirements set by both the validator and the network.
Contributors' checklist...
BREAKING CHANGE: xxx
message was included in the description